#include "ClkInitHelper.h"


#define BSP_XTAL32_PORT (GPIO_PORT_C)
#define BSP_XTAL32_IN_PIN (GPIO_PIN_15)
#define BSP_XTAL32_OUT_PIN (GPIO_PIN_14)

//Clock Config
// 使用内部高速时钟
// 注意，修改频率还需要修改system_hc32f4a0.h中的XTAL_VALUE
// 有源高速晶振需要修改stcXtalInit.u8Mode = CLK_XTAL_MD_OSC;为stcXtalInit.u8Mode = CLK_XTAL_MD_EXCLK;

// 使用外部高速有源时钟
void App_ClkCfg(void) {
  /* Set bus clock div. */
  CLK_SetClockDiv(CLK_BUS_CLK_ALL, (CLK_HCLK_DIV1 | CLK_EXCLK_DIV2 | CLK_PCLK0_DIV1 | CLK_PCLK1_DIV2 |
                                    CLK_PCLK2_DIV4 | CLK_PCLK3_DIV4 | CLK_PCLK4_DIV2));
  /* sram init include read/write wait cycle setting */
  SRAM_SetWaitCycle(SRAM_SRAM_ALL, SRAM_WAIT_CYCLE1, SRAM_WAIT_CYCLE1);
  SRAM_SetWaitCycle(SRAM_SRAMH, SRAM_WAIT_CYCLE0, SRAM_WAIT_CYCLE0);
  /* flash read wait cycle setting */
  EFM_SetWaitCycle(EFM_WAIT_CYCLE5);
	EFM_CacheCmd(ENABLE);
  /* XTAL config */
  stc_clock_xtal_init_t stcXtalInit;
  (void) CLK_XtalStructInit(&stcXtalInit);
  stcXtalInit.u8State = CLK_XTAL_ON;
  stcXtalInit.u8Drv = CLK_XTAL_DRV_HIGH;
  stcXtalInit.u8Mode = CLK_XTAL_MD_OSC;
  stcXtalInit.u8StableTime = CLK_XTAL_STB_2MS;
  (void) CLK_XtalInit(&stcXtalInit);
  /* MPLL config */
  stc_clock_pll_init_t stcMPLLInit;
  (void) CLK_PLLStructInit(&stcMPLLInit);
  stcMPLLInit.PLLCFGR = 0UL;
  stcMPLLInit.PLLCFGR_f.PLLM = (5UL - 1UL);
  stcMPLLInit.PLLCFGR_f.PLLN = (80UL - 1UL);
  stcMPLLInit.PLLCFGR_f.PLLP = (2UL - 1UL);
  stcMPLLInit.PLLCFGR_f.PLLQ = (16UL - 1UL);
  stcMPLLInit.PLLCFGR_f.PLLR = (16UL - 1UL);
  stcMPLLInit.u8PLLState = CLK_PLL_ON;
  stcMPLLInit.PLLCFGR_f.PLLSRC = CLK_PLL_SRC_XTAL;
  (void) CLK_PLLInit(&stcMPLLInit);
  /* UPLL config */
  stc_clock_pllx_init_t stcUPLLInit;
  (void) CLK_PLLxStructInit(&stcUPLLInit);
  stcUPLLInit.PLLCFGR = 0UL;
  stcUPLLInit.PLLCFGR_f.PLLM = (5UL - 1UL);
  stcUPLLInit.PLLCFGR_f.PLLN = (48UL - 1UL);
  stcUPLLInit.PLLCFGR_f.PLLP = (4UL - 1UL);
  stcUPLLInit.PLLCFGR_f.PLLQ = (16UL - 1UL);
  stcUPLLInit.PLLCFGR_f.PLLR = (5UL - 1UL);
  stcUPLLInit.u8PLLState = CLK_PLLX_ON;
  (void) CLK_PLLxInit(&stcUPLLInit);
  /* 3 cycles for 126MHz ~ 200MHz */
  GPIO_SetReadWaitCycle(GPIO_RD_WAIT3);
  /* Switch driver ability */
  PWC_HighSpeedToHighPerformance();
  /* Set the system clock source */
  CLK_SetSysClockSrc(CLK_SYSCLK_SRC_PLL);
  /* Specifies the clock source of ADC. */
  CLK_SetPeriClockSrc(CLK_PERIPHCLK_PLLXP);
}

// rt的tick处理函数。原本在board.c中，本处被移动到此
void rt_os_tick_callback(void) {
  rt_interrupt_enter();

  rt_tick_increase();

  rt_interrupt_leave();
}

/**
 * @brief  SysTick interrupt handler function.
 * @param  None
 * @retval None
 */
void SysTick_Handler(void) {
  // 初始化rt的tick
  rt_os_tick_callback();
  //    SysTick_IncTick();

  //    __DSB();  /* Arm Errata 838869 */
}

int32_t BSP_XTAL32_Init(void) {
  stc_clock_xtal32_init_t stcXtal32Init;
  stc_fcm_init_t stcFcmInit;
  uint32_t u32TimeOut = 0UL;
  uint32_t u32Time = HCLK_VALUE / 5UL;

  if (CLK_XTAL32_ON == READ_REG8(CM_CMU->XTAL32CR)) {
    /* Disable xtal32 */
    (void) CLK_Xtal32Cmd(DISABLE);
    /* Wait 5 * xtal32 cycle */
    DDL_DelayUS(160U);
  }

  /* Xtal32 config */
  (void) CLK_Xtal32StructInit(&stcXtal32Init);
  stcXtal32Init.u8State = CLK_XTAL32_ON;
  stcXtal32Init.u8Drv = CLK_XTAL32_DRV_MID;
  stcXtal32Init.u8Filter = CLK_XTAL32_FILTER_ALL_MD;
  GPIO_AnalogCmd(BSP_XTAL32_PORT, BSP_XTAL32_IN_PIN | BSP_XTAL32_OUT_PIN, ENABLE);
  (void) CLK_Xtal32Init(&stcXtal32Init);

  /* FCM config */
  FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_FCM, ENABLE);
  (void) FCM_StructInit(&stcFcmInit);
  stcFcmInit.u32RefClock = FCM_REF_CLK_MRC;
  stcFcmInit.u32RefClockDiv = FCM_REF_CLK_DIV8192;
  stcFcmInit.u32RefClockEdge = FCM_REF_CLK_RISING;
  stcFcmInit.u32TargetClock = FCM_TARGET_CLK_XTAL32;
  stcFcmInit.u32TargetClockDiv = FCM_TARGET_CLK_DIV1;
  stcFcmInit.u16LowerLimit = (uint16_t) ((XTAL32_VALUE / (MRC_VALUE / 8192U)) * 96UL / 100UL);
  stcFcmInit.u16UpperLimit = (uint16_t) ((XTAL32_VALUE / (MRC_VALUE / 8192U)) * 104UL / 100UL);
  (void) FCM_Init(&stcFcmInit);
  /* Enable FCM, to ensure xtal32 stable */
  FCM_Cmd(ENABLE);
  for (;;) {
    if (SET == FCM_GetStatus(FCM_FLAG_END)) {
      FCM_ClearStatus(FCM_FLAG_END);
      if ((SET == FCM_GetStatus(FCM_FLAG_ERR)) || (SET == FCM_GetStatus(FCM_FLAG_OVF))) {
        FCM_ClearStatus(FCM_FLAG_ERR | FCM_FLAG_OVF);
      } else {
        (void) FCM_DeInit();
        FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_FCM, DISABLE);
        // 这里必须延迟，目的是等候晶振输入完全稳定，因此有源晶振也必须等候。
        DDL_DelayMS(1);
        return LL_OK;
      }
    }
    u32TimeOut++;
    if (u32TimeOut > u32Time) {
      (void) FCM_DeInit();
      FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_FCM, DISABLE);
      return LL_ERR_TIMEOUT;
    }
  }
}
